package com.masz.distributedlock;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

/**
 * @Author msz
 * @Date 2021/11/22 10:54 上午
 * 表达式计算
 */
public class ExpressionCalcUtil {

    /**
     * 支持运算符
     */
    public static Set<Character> oper = new HashSet<>(1);

    private final static char ADD = '+';
    private final static char REDUCE = '-';
    private final static char MULTI = '*';
    private final static char DIVIDE = '/';
    private final static char LEFT_PARENTHESES = '(';
    private final static char RIGHT_PARENTHESES = ')';


    static {
        oper.add(ADD);
        oper.add(REDUCE);
        oper.add(MULTI);
        oper.add(DIVIDE);
        oper.add(LEFT_PARENTHESES);
        oper.add(RIGHT_PARENTHESES);
    }

    /**
     * 计算表达式的值
     * 不支持负数，可以通过 6*(0-9) 来处理负数问题
     * @param str
     * @return
     */
    public static double calcExpression(String str) {
        char[] arrayA = str.toCharArray();
        //运算数字及表达式计算结果
        Stack<Double> number = new Stack<Double>();
        //运算符
        Stack<Character> operator = new Stack<Character>();

        String numberStr = "";

        // 前一个char
        Character last = null;

        for (int i = 0; i < arrayA.length; i++) {
            if(i == arrayA.length - 1 && !oper.contains(arrayA[i])){
                numberStr+=arrayA[i];
                number.push(Double.valueOf(numberStr));
                break;
            }

            boolean b = oper.contains(arrayA[i]);
            if (!b) {
                // 不是计算符号，拼接数值字符串
                numberStr += arrayA[i];
            } else {
                System.out.println(numberStr);
                // 是计算符号
                if (numberStr != null && numberStr.length() > 0) {
                    number.push(Double.valueOf(numberStr));
                    numberStr = "";
                }

                if (operator.empty()) {
                    operator.push(arrayA[i]);
                } else {
                    //进栈前，存放运算符栈中栈顶存放字符
                    char temp1 = operator.pop();
                    //比较当前字符与栈顶字符优先级
                    int judge = comparePriority(temp1, arrayA[i]);
                    if (judge == 1) {
                        //当前字符优先级小于栈顶字符
                        double tempA = number.pop();
                        double tempB = number.pop();
                        double result = computeNumber(tempB, tempA, temp1);
                        number.push(result);
                        operator.push(arrayA[i]);
                    } else if (judge == 0) {
                        //当前字符优先级大于栈顶字符
                        operator.push(temp1);
                        operator.push(arrayA[i]);
                    } else if (judge == 3) {
                        //此时')'刚好准备进栈
                        while (temp1 != LEFT_PARENTHESES) {
                            //')'字符要等到第一个'('出栈才能结束循环
                            double tempA = number.pop();
                            double tempB = number.pop();
                            double result = computeNumber(tempB, tempA, temp1);
                            number.push(result);
                            temp1 = operator.pop();
                        }
                    }
                }
            }
        }

        while (!operator.empty() && !number.empty()) {
            //此时，字符栈中还存在运算符的情况
            char temp1 = operator.pop();
            double tempA = number.pop();
            double tempB = number.pop();
            double result = computeNumber(tempB, tempA, temp1);
            number.push(result);
        }
        return number.pop();
    }

    /**
     * 计算a operator b的值，operator = {+,-,*,/}
     * @param a
     * @param b
     * @param operator
     * @return
     */
    public static double computeNumber(double a, double b, char operator) {
        double result;
        switch (operator) {
            case ADD:
                result = a + b;
                break;
            case REDUCE:
                result = a - b;
                break;
            case MULTI:
                result = a * b;
                break;
            case DIVIDE:
                result = BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), 2, RoundingMode.DOWN).doubleValue();
                break;
            default:
                result = 0;
                break;
        }
        return result;
    }

    /**
     * 判断运算符a和b的优先级
     * a 是行，b 是列
     *    +  -  *  /  (   )
     *  + 1  1  0  0  0  3
     *  - 1  1  0  0  0  3
     *  * 1  1  1  1  0  3
     *  \ 1  1  1  1  0  3
     *  ( 0  0  0  0  0  2
     *  )-1 -1 -1 -1 -1 -1
     *
     * @param a
     * @param b
     * @return
     */
    public static int comparePriority(char a, char b) {
        //使用二维数组表达运算符之间的优先级，行用字符a表示，列用字符b表示
        int[][] value = {{1, 1, 0, 0, 0, 3},
                {1, 1, 0, 0, 0, 3},
                {1, 1, 1, 1, 0, 3},
                {1, 1, 1, 1, 0, 3},
                {0, 0, 0, 0, 0, 2},
                {-1, -1, -1, -1, -1, -1}};
        int i = 0;
        int j = 0;
        if (a == ADD) {
            i = 0;
        } else if (a == REDUCE) {
            i = 1;
        } else if (a == MULTI) {
            i = 2;
        } else if (a == DIVIDE) {
            i = 3;
        } else if (a == LEFT_PARENTHESES) {
            i = 4;
        } else if (a == RIGHT_PARENTHESES) {
            i = 5;
        }

        if (b == ADD) {
            j = 0;
        } else if (b == REDUCE) {
            j = 1;
        } else if (b == MULTI) {
            j = 2;
        } else if (b == DIVIDE) {
            j = 3;
        } else if (b == LEFT_PARENTHESES) {
            j = 4;
        } else if (b == RIGHT_PARENTHESES) {
            j = 5;
        }
        return value[i][j];
    }

    public static void main(String[] args) {
        String s = "(0-5)-4*((0-9)+3)";
        System.out.println(calcExpression(s));
    }


}
